package com.one.wishes.ui;

import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import com.google.android.material.behavior.SwipeDismissBehavior;
import com.one.wishes.R;

public final class TSnackbar {

	public static abstract class Callback {

		public static final int DISMISS_EVENT_SWIPE = 0;

		public static final int DISMISS_EVENT_ACTION = 1;

		public static final int DISMISS_EVENT_TIMEOUT = 2;

		public static final int DISMISS_EVENT_MANUAL = 3;

		public static final int DISMISS_EVENT_CONSECUTIVE = 4;

		@IntDef({ DISMISS_EVENT_SWIPE, DISMISS_EVENT_ACTION,
				DISMISS_EVENT_TIMEOUT, DISMISS_EVENT_MANUAL,
				DISMISS_EVENT_CONSECUTIVE })
		@Retention(RetentionPolicy.SOURCE)
		public @interface DismissEvent {
		}

		public void onDismissed(TSnackbar TSnackbar, @DismissEvent int event) {

		}

		public void onShown(TSnackbar TSnackbar) {

		}
	}

	@IntDef({ LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG })
	@Retention(RetentionPolicy.SOURCE)
	public @interface Duration {
	}

	public static final int LENGTH_INDEFINITE = -2;

	public static final int LENGTH_SHORT = -1;

	public static final int LENGTH_LONG = 0;
	private static final int ANIMATION_DURATION = 250;
	private static final Handler sHandler;
	private static final int MSG_SHOW = 0;
	private static final int MSG_DISMISS = 1;

	static {
		sHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
			@Override
			public boolean handleMessage(Message message) {
				switch (message.what) {
				case MSG_SHOW:
					((TSnackbar) message.obj).showView();
					return true;
				case MSG_DISMISS:
					((TSnackbar) message.obj).hideView(message.arg1);
					return true;
				}
				return false;
			}
		});
	}

	private final ViewGroup mParent;
	private final Context mContext;
	private final SnackbarLayout mView;
	private int mDuration;
	private Callback mCallback;

	private TSnackbar(ViewGroup parent) {
		mParent = parent;
		mContext = parent.getContext();
		LayoutInflater inflater = LayoutInflater.from(mContext);
		mView = (SnackbarLayout) inflater.inflate(R.layout.tsnackbar_layout,
				mParent, false);
	}

	@NonNull
	public static TSnackbar make(@NonNull View view,
			@NonNull CharSequence text, @Duration int duration) {
		TSnackbar TSnackbar = new TSnackbar(findSuitableParent(view));
		TSnackbar.setText(text);
		TSnackbar.setDuration(duration);
		return TSnackbar;
	}

	@NonNull
	public static TSnackbar make(@NonNull View view, @StringRes int resId,
			@Duration int duration) {
		return make(view, view.getResources().getText(resId), duration);
	}

	private static ViewGroup findSuitableParent(View view) {
		return (ViewGroup) view;
	}

	/**
	 * size should be around 200....
	 */
	@SuppressWarnings("deprecation")
	public TSnackbar addIcon(int resource_id, int size) {
		final TextView tv = mView.getMessageView();

		tv.setCompoundDrawablesWithIntrinsicBounds(
				new BitmapDrawable(Bitmap.createScaledBitmap(
						((BitmapDrawable) (mContext.getResources()
								.getDrawable(resource_id))).getBitmap(), size,
						size, true)), null, null, null);

		return this;
	}

	public static float convertDpToPixel(float dp, Context context) {
		Resources resources = context.getResources();
		DisplayMetrics metrics = resources.getDisplayMetrics();
		float px = dp * (metrics.densityDpi / 160f);
		return px;
	}

	public static float convertPixelsToDp(float px, Context context) {
		Resources resources = context.getResources();
		DisplayMetrics metrics = resources.getDisplayMetrics();
		float dp = px / (metrics.densityDpi / 160f);
		return dp;
	}

	@NonNull
	public TSnackbar setAction(@StringRes int resId,
			View.OnClickListener listener) {
		return setAction(mContext.getText(resId), listener);
	}

	@NonNull
	public TSnackbar setAction(CharSequence text,
			final View.OnClickListener listener) {
		final TextView tv = mView.getActionView();
		if (TextUtils.isEmpty(text) || listener == null) {
			tv.setVisibility(View.GONE);
			tv.setOnClickListener(null);
		} else {
			tv.setVisibility(View.VISIBLE);
			tv.setText(text);
			tv.setOnClickListener(new View.OnClickListener() {
				@Override
				public void onClick(View view) {
					listener.onClick(view);

					dispatchDismiss(Callback.DISMISS_EVENT_ACTION);
				}
			});
		}
		return this;
	}

	@NonNull
	public TSnackbar setActionTextColor(ColorStateList colors) {
		final TextView tv = mView.getActionView();
		tv.setTextColor(colors);
		return this;
	}

	@NonNull
	public TSnackbar setActionTextColor(@ColorInt int color) {
		final TextView tv = mView.getActionView();
		tv.setTextColor(color);
		return this;
	}

	@NonNull
	public TSnackbar setText(@NonNull CharSequence message) {
		final TextView tv = mView.getMessageView();
		tv.setText(message);
		return this;
	}

	@NonNull
	public TSnackbar setText(@StringRes int resId) {
		return setText(mContext.getText(resId));
	}

	@NonNull
	public TSnackbar setDuration(@Duration int duration) {
		mDuration = duration;
		return this;
	}

	@Duration
	public int getDuration() {
		return mDuration;
	}

	@NonNull
	public View getView() {
		return mView;
	}

	public void show() {
		SnackbarManager.getInstance().show(mDuration, mManagerCallback);
	}

	public void dismiss() {
		dispatchDismiss(Callback.DISMISS_EVENT_MANUAL);
	}

	private void dispatchDismiss(@Callback.DismissEvent int event) {
		SnackbarManager.getInstance().dismiss(mManagerCallback, event);
	}

	@NonNull
	public TSnackbar setCallback(Callback callback) {
		mCallback = callback;
		return this;
	}

	public boolean isShown() {
		return mView.isShown();
	}

	private final SnackbarManager.Callback mManagerCallback = new SnackbarManager.Callback() {
		@Override
		public void show() {
			sHandler.sendMessage(sHandler.obtainMessage(MSG_SHOW,
					TSnackbar.this));
		}

		@Override
		public void dismiss(int event) {
			sHandler.sendMessage(sHandler.obtainMessage(MSG_DISMISS, event, 0,
					TSnackbar.this));
		}
	};

	final void showView() {
		if (mView.getParent() == null) {
			final ViewGroup.LayoutParams lp = mView.getLayoutParams();
			if (lp instanceof CoordinatorLayout.LayoutParams) {

				final Behavior behavior = new Behavior();
				behavior.setStartAlphaSwipeDistance(0.1f);
				behavior.setEndAlphaSwipeDistance(0.6f);
				behavior.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_START_TO_END);
				behavior.setListener(new SwipeDismissBehavior.OnDismissListener() {
					@Override
					public void onDismiss(View view) {
						dispatchDismiss(Callback.DISMISS_EVENT_SWIPE);
					}

					@Override
					public void onDragStateChanged(int state) {
						switch (state) {
						case SwipeDismissBehavior.STATE_DRAGGING:
						case SwipeDismissBehavior.STATE_SETTLING:

							SnackbarManager.getInstance().cancelTimeout(
									mManagerCallback);
							break;
						case SwipeDismissBehavior.STATE_IDLE:

							SnackbarManager.getInstance().restoreTimeout(
									mManagerCallback);
							break;
						}
					}
				});
				((CoordinatorLayout.LayoutParams) lp).setBehavior(behavior);
			}
			mParent.addView(mView);
		}
		if (ViewCompat.isLaidOut(mView)) {

			animateViewIn();
		} else {

			mView.setOnLayoutChangeListener(new SnackbarLayout.OnLayoutChangeListener() {
				@Override
				public void onLayoutChange(View view, int left, int top,
						int right, int bottom) {
					animateViewIn();
					mView.setOnLayoutChangeListener(null);
				}
			});
		}
	}

	private void animateViewIn() {

		Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
				R.anim.top_in);
		anim.setInterpolator(com.one.wishes.ui.AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
		anim.setDuration(ANIMATION_DURATION);
		anim.setAnimationListener(new Animation.AnimationListener() {
			@Override
			public void onAnimationEnd(Animation animation) {
				if (mCallback != null) {
					mCallback.onShown(TSnackbar.this);
				}
				SnackbarManager.getInstance().onShown(mManagerCallback);
			}

			@Override
			public void onAnimationStart(Animation animation) {
			}

			@Override
			public void onAnimationRepeat(Animation animation) {
			}
		});
		mView.startAnimation(anim);

	}

	private void animateViewOut(final int event) {

		Animation anim = AnimationUtils.loadAnimation(mView.getContext(),
				R.anim.top_out);
		anim.setInterpolator(com.one.wishes.ui.AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
		anim.setDuration(ANIMATION_DURATION);
		anim.setAnimationListener(new Animation.AnimationListener() {
			@Override
			public void onAnimationEnd(Animation animation) {
				onViewHidden(event);
			}

			@Override
			public void onAnimationStart(Animation animation) {
			}

			@Override
			public void onAnimationRepeat(Animation animation) {
			}
		});
		mView.startAnimation(anim);

	}

	final void hideView(int event) {
		if (mView.getVisibility() != View.VISIBLE || isBeingDragged()) {
			onViewHidden(event);
		} else {
			animateViewOut(event);
		}
	}

	private void onViewHidden(int event) {

		mParent.removeView(mView);

		if (mCallback != null) {
			mCallback.onDismissed(this, event);
		}

		SnackbarManager.getInstance().onDismissed(mManagerCallback);
	}

	@SuppressWarnings("rawtypes")
	private boolean isBeingDragged() {
		final ViewGroup.LayoutParams lp = mView.getLayoutParams();
		if (lp instanceof CoordinatorLayout.LayoutParams) {
			final CoordinatorLayout.LayoutParams cllp = (CoordinatorLayout.LayoutParams) lp;
			final CoordinatorLayout.Behavior behavior = cllp.getBehavior();
			if (behavior instanceof SwipeDismissBehavior) {
				return ((SwipeDismissBehavior) behavior).getDragState() != SwipeDismissBehavior.STATE_IDLE;
			}
		}
		return false;
	}

	public static class SnackbarLayout extends LinearLayout {
		private TextView mMessageView;
		private Button mActionView;
		private int mMaxWidth;
		private int mMaxInlineActionWidth;

		interface OnLayoutChangeListener {
			public void onLayoutChange(View view, int left, int top, int right,
					int bottom);
		}

		private OnLayoutChangeListener mOnLayoutChangeListener;

		public SnackbarLayout(Context context) {
			this(context, null);
		}

		public SnackbarLayout(Context context, AttributeSet attrs) {
			super(context, attrs);
			TypedArray a = context.obtainStyledAttributes(attrs,
					R.styleable.SnackbarLayout);
			mMaxWidth = a.getDimensionPixelSize(
					R.styleable.SnackbarLayout_android_maxWidth, -1);
			mMaxInlineActionWidth = a.getDimensionPixelSize(
					R.styleable.SnackbarLayout_maxActionInlineWidth, -1);
			if (a.hasValue(R.styleable.SnackbarLayout_elevation)) {
				ViewCompat.setElevation(this, a.getDimensionPixelSize(
						R.styleable.SnackbarLayout_elevation, 0));
			}
			a.recycle();
			setClickable(true);

			LayoutInflater.from(context).inflate(
					R.layout.tsnackbar_layout_include, this);
		}

		@Override
		protected void onFinishInflate() {
			super.onFinishInflate();
			mMessageView = (TextView) findViewById(R.id.snackbar_text);
			mActionView = (Button) findViewById(R.id.snackbar_action);
		}

		TextView getMessageView() {
			return mMessageView;
		}

		Button getActionView() {
			return mActionView;
		}

		@Override
		protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
			super.onMeasure(widthMeasureSpec, heightMeasureSpec);
			if (mMaxWidth > 0 && getMeasuredWidth() > mMaxWidth) {
				widthMeasureSpec = MeasureSpec.makeMeasureSpec(mMaxWidth,
						MeasureSpec.EXACTLY);
				super.onMeasure(widthMeasureSpec, heightMeasureSpec);
			}
			final int multiLineVPadding = getResources().getDimensionPixelSize(
					R.dimen.design_snackbar_padding_vertical_2lines);
			final int singleLineVPadding = getResources()
					.getDimensionPixelSize(
							R.dimen.design_snackbar_padding_vertical);
			final boolean isMultiLine = mMessageView.getLayout().getLineCount() > 1;
			boolean remeasure = false;
			if (isMultiLine && mMaxInlineActionWidth > 0
					&& mActionView.getMeasuredWidth() > mMaxInlineActionWidth) {
				if (updateViewsWithinLayout(VERTICAL, multiLineVPadding,
						multiLineVPadding - singleLineVPadding)) {
					remeasure = true;
				}
			} else {
				final int messagePadding = isMultiLine ? multiLineVPadding
						: singleLineVPadding;
				if (updateViewsWithinLayout(HORIZONTAL, messagePadding,
						messagePadding)) {
					remeasure = true;
				}
			}
			if (remeasure) {
				super.onMeasure(widthMeasureSpec, heightMeasureSpec);
			}
		}

		void animateChildrenIn(int delay, int duration) {
			ViewCompat.setAlpha(mMessageView, 0f);
			ViewCompat.animate(mMessageView).alpha(1f).setDuration(duration)
					.setStartDelay(delay).start();
			if (mActionView.getVisibility() == VISIBLE) {
				ViewCompat.setAlpha(mActionView, 0f);
				ViewCompat.animate(mActionView).alpha(1f).setDuration(duration)
						.setStartDelay(delay).start();
			}
		}

		void animateChildrenOut(int delay, int duration) {
			ViewCompat.setAlpha(mMessageView, 1f);
			ViewCompat.animate(mMessageView).alpha(0f).setDuration(duration)
					.setStartDelay(delay).start();
			if (mActionView.getVisibility() == VISIBLE) {
				ViewCompat.setAlpha(mActionView, 1f);
				ViewCompat.animate(mActionView).alpha(0f).setDuration(duration)
						.setStartDelay(delay).start();
			}
		}

		@Override
		protected void onLayout(boolean changed, int l, int t, int r, int b) {
			super.onLayout(changed, l, t, r, b);
			if (changed && mOnLayoutChangeListener != null) {
				mOnLayoutChangeListener.onLayoutChange(this, l, t, r, b);
			}
		}

		void setOnLayoutChangeListener(
				OnLayoutChangeListener onLayoutChangeListener) {
			mOnLayoutChangeListener = onLayoutChangeListener;
		}

		private boolean updateViewsWithinLayout(final int orientation,
				final int messagePadTop, final int messagePadBottom) {
			boolean changed = false;
			if (orientation != getOrientation()) {
				setOrientation(orientation);
				changed = true;
			}
			if (mMessageView.getPaddingTop() != messagePadTop
					|| mMessageView.getPaddingBottom() != messagePadBottom) {
				updateTopBottomPadding(mMessageView, messagePadTop,
						messagePadBottom);
				changed = true;
			}
			return changed;
		}

		private static void updateTopBottomPadding(View view, int topPadding,
				int bottomPadding) {
			if (ViewCompat.isPaddingRelative(view)) {
				ViewCompat.setPaddingRelative(view,
						ViewCompat.getPaddingStart(view), topPadding,
						ViewCompat.getPaddingEnd(view), bottomPadding);
			} else {
				view.setPadding(view.getPaddingLeft(), topPadding,
						view.getPaddingRight(), bottomPadding);
			}
		}
	}

	final class Behavior extends SwipeDismissBehavior<SnackbarLayout> {
		@Override
		public boolean onInterceptTouchEvent(CoordinatorLayout parent,
				SnackbarLayout child, MotionEvent event) {

			if (parent.isPointInChildBounds(child, (int) event.getX(),
					(int) event.getY())) {
				switch (event.getActionMasked()) {
				case MotionEvent.ACTION_DOWN:
					SnackbarManager.getInstance().cancelTimeout(
							mManagerCallback);
					break;
				case MotionEvent.ACTION_UP:
				case MotionEvent.ACTION_CANCEL:
					SnackbarManager.getInstance().restoreTimeout(
							mManagerCallback);
					break;
				}
			}
			return super.onInterceptTouchEvent(parent, child, event);
		}
	}
}